package com.jweb.framework.http;

import org.springframework.web.servlet.FrameworkServlet;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SourceFilteringListener;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.core.OrderComparator;
import org.springframework.ui.context.ThemeSource;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ModelAndViewDefiningException;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
import org.springframework.web.util.NestedServletException;
import org.springframework.web.util.WebUtils;
import com.jweb.framework.core.channel.MessagesProvider;
import com.jweb.framework.http.support.DefaultRefererResolver;
import com.jweb.framework.http.support.MimeTypesViewResolver;
import com.jweb.framework.http.support.SmartLocaleResolver;
import com.jweb.framework.http.support.SmartThemeResolver;
import com.jweb.framework.http.support.Utils;

/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明： 对Spring DispatcherServlet重新编写，主要是处理HTTP渠道的请求及响应
 */
public class MainServlet extends FrameworkServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1819094615562501440L;
	
	private static final String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
	
	private static final String mainServletHandler = MainServlet.class.getName() + ".HANDLER";
	
	public static final String MESSAGES_PROVIDER_ATTRIBUTE = MainServlet.class.getName() + ".MESSAGES_PROVIDER";
	
	public static final String REFERER_RESOLVER_ATTRIBUTE = MainServlet.class.getName() + ".REFERER_RESOLVER";
	
	public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
	
	public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCAL_RESOLVER";
	
	public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
	
	public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
	
	public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
	
	protected static final Logger pageNotFoundLogger = LoggerFactory.getLogger("org.springframework.web.servlet.PageNotFound");
	
	private boolean cleanupAfterIncludeFlag = true ;
	private MultipartResolver multipartResolver ;
	private LocaleResolver localeResolver ;
	private RefererResolver refererResolver ;
	private ThemeResolver themeResolver ;
	private List<HandlerMapping> handlerMappings ;
	private List<HandlerAdapter> handlerAdapters ;
	private List<HandlerExceptionResolver> handlerExceptionResolvers ;
	private List<ViewResolver> viewResolvers ;
	private MessagesProvider messagesProvider ;
	private boolean useMimeViewResolverFlag = true ;
	
	
	public void setCleanupAfterInclude(boolean flag){
		cleanupAfterIncludeFlag = flag ;
	}
	
	public void setUseMimeViewResolver(boolean flag){
		useMimeViewResolverFlag = flag ;
	}
	

	@Override
	protected void doService(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
		if(logger.isDebugEnabled()){ ///weather/downloadFiles.do
			String requestUri = Utils.getPathHelper().getRequestUri(request); // requestUri="/weather/fileUpload.do"  , requestUri="/weather/services"
			logger.debug("MainServlet with name '" + getServletName() + "' processing " + request.getMethod() + " request for [" + requestUri + "]");
		}
		
		Map<String, Object> attributesSnapshot = null ;
		if(WebUtils.isIncludeRequest(request)){
			logger.debug("Taking snapshot of request attributes before includes . ");
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration attrNames = request.getAttributeNames();
			while(attrNames.hasMoreElements()){
				String attrName = (String) attrNames.nextElement();
				if(cleanupAfterIncludeFlag || attrName.startsWith("org.springframework.web.servlet")){
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}
		
		resetRequestAttributes(request);
		
		try {
			
			doDispatch(request , response);
			
		} finally{
			if(attributesSnapshot!=null){
				restoreRequestAttributes(request , attributesSnapshot);
			}
		}

	}
	
	/** 对HTTP渠道统一分发处理 */
	private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		HttpServletRequest processedRequest = request ;
		HandlerExecutionChain mappedHandler = null ; 
		int interceptorIndex = -1 ;
		
		/**
		 * 支持对异步的处理
		 */
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request) ;
		
		try {
			
			ModelAndView mv ;
			boolean errorView = false ; 
			
			try {
				processedRequest = checkMultipart(request);// 如果是文件上传，那processedRequest=DefaultMultipartHttpServletRequest
				
				mappedHandler = getHandler(request , false);
				if(mappedHandler == null || mappedHandler.getHandler()==null){
					noHandlerFound(processedRequest , response);
					
					if(processedRequest != request){
						cleanupMultipart(processedRequest);
					}
					return ;
				}
				
				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
				if(interceptors!=null){
					for (int i = 0 ; i < interceptors.length ; i++){
						HandlerInterceptor interceptor = interceptors[i];
						if(!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())){
							triggerAfterCompletion(mappedHandler , interceptorIndex , processedRequest , response , null);
							
							if(processedRequest != request){
								cleanupMultipart(processedRequest);
							}
							return ;
						}
						interceptorIndex = i ;
					}
				}
				
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				
				try {
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				} finally {
					if(asyncManager.isConcurrentHandlingStarted()){
						return ;
					}
				}
				
				if(mv!=null && !mv.hasView()){
					throw new IllegalArgumentException("ModelAndView has no view . ");
				}
				
				if(interceptors != null){
					for(int i = interceptors.length-1 ; i>=0 ; i--){
						HandlerInterceptor interceptor = interceptors[i];
						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
					}
				}
				
			} catch (ModelAndViewDefiningException ex) {
				logger.debug("ModelAndViewDefiningException encountered",ex);
				mv = ex.getModelAndView();
			} catch (Exception ex) {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(processedRequest, response , handler , ex);
				errorView = (mv!=null);
			}
			
			if(mv != null && !mv.wasCleared()){ // true
				render(mv , processedRequest , response);
				if(errorView){
					WebUtils.clearErrorRequestAttributes(request);
				}
			}
			else {
				if(logger.isDebugEnabled()){
					logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "':assuming HandlerAdapter completed request handling . ");
				}
			}
			
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
		} catch (Exception e) {
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, e);
			throw e;
		} catch (Error e){
			ServletException ex = new NestedServletException("handler procesing failed ", e);
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw e;
		}
		finally{
			if(asyncManager.isConcurrentHandlingStarted()){
				// 不在同一个包内，所以调用不了，此处只要不用实现异步拦截器，就不会有问题。
				//mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return ;
			}
			
			if(processedRequest != request){
				cleanupMultipart(processedRequest);
			}
		}
	}

	

	private void render(ModelAndView mv, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
		Locale locale = localeResolver.resolveLocale(request); // "locale"=Locale
		response.setLocale(locale);
		
		
		View view ; 
		if(mv.isReference()){ // true
			view = resolveViewName(mv.getViewName(),mv.getModel(),locale,request);//view=JstlView
			if(view == null){
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'");
			}
		}
		else{
			view = mv.getView();
			if(view == null){
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + getServletName() + "'");
			}
		}
		
		if(logger.isDebugEnabled()){
			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
		}
		
		view.render(mv.getModel(), request, response);
	}

	private View resolveViewName(String viewName, Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception{
		
		for(ViewResolver viewResolver : viewResolvers){
			View view = viewResolver.resolveViewName(viewName, locale); // "viewResolver"=MimeTypesViewResolver
			if(view != null){
				return view ;
			}
		}
		
		return null;
	}

	private ModelAndView processHandlerException(
			HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception ex) throws Exception{
		
		ModelAndView exMv = null ; 
		for (HandlerExceptionResolver handlerExceptionResolver : handlerExceptionResolvers){
			exMv = handlerExceptionResolver.resolveException(request, response, handlerExceptionResolver, ex);
			if(exMv != null){
				break ;
			}
		}
		
		if(exMv != null){
			if(exMv.isEmpty()){
				return null;
			}
			if(logger.isDebugEnabled()){
				logger.debug("Handler execution resulted in exception - forwarding to resolved error view : " + exMv , ex);
			}
			WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
			return exMv ;
		}
		
		throw ex;
	}

	private HandlerAdapter getHandlerAdapter(Object handler) throws ServletException{
		
		for (HandlerAdapter ha : handlerAdapters){
			if(logger.isTraceEnabled()){
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if(ha.supports(handler)){
				return ha ;
			}
		}
		
		throw new ServletException("No adapter for handler [" + handler + "]:Does your handler implements a supported interface like Controller ?");
	}

	private void triggerAfterCompletion(HandlerExecutionChain mappedHandler,
			int interceptorIndex, HttpServletRequest request,
			HttpServletResponse response, Exception ex) throws Exception {
		
		if(mappedHandler != null){
			HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
			if(interceptors != null){
				for (int i = interceptorIndex ; i>=0 ;i--){
					HandlerInterceptor interceptor = interceptors[i];
					try {
						interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
					} catch (Throwable ex2) {
						logger.error("HandlerInterceptor.afterCompletion throw exception ", ex2);
					}
				}
			}
		}
		
	}

	private void cleanupMultipart(HttpServletRequest processedRequest) {
		
		if(processedRequest instanceof MultipartHttpServletRequest){
			multipartResolver.cleanupMultipart((MultipartHttpServletRequest) processedRequest);
		}
		
	}

	private void noHandlerFound(HttpServletRequest processedRequest,
			HttpServletResponse response) throws Exception{
		
		if(pageNotFoundLogger.isWarnEnabled()){
			String requestUri = Utils.getPathHelper().getRequestUri(processedRequest);
			pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + requestUri + "] in MainServlet with name '" + getServletName() + "'");
		}
		
		response.sendError(404);
	}

	private HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception{
		
		HandlerExecutionChain handlerExecutionChain = (HandlerExecutionChain) request.getAttribute(mainServletHandler);
		if(handlerExecutionChain != null){
			if(!cache){
				request.removeAttribute(mainServletHandler);
			}
			return handlerExecutionChain ;
		}
		
		for(HandlerMapping hm : handlerMappings){
			if(logger.isTraceEnabled()){
				logger.trace("Testing handler map [" + hm + "] in MainServlet with name '" + getServletName() + "'");
			}
			handlerExecutionChain = hm.getHandler(request);
			
			if(handlerExecutionChain != null){
				if(cache){
					request.setAttribute(mainServletHandler, handlerExecutionChain);
				}
				return handlerExecutionChain ;
			}
		}
		
		return null;
	}

	private HttpServletRequest checkMultipart(HttpServletRequest request) {
		
		//System.out.println("【【【【【【【【【【【 contentType = " + request.getContentType());
		
		if(multipartResolver != null && multipartResolver.isMultipart(request)){
			if(multipartResolver instanceof MultipartHttpServletRequest){
				logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward , this typically results from an additional MultipartFilter in web.xml . ");
			}
			else{  // true
				return multipartResolver.resolveMultipart(request); // "multipartResolver"=CommonsMultipartResolver
			}
		}
		
		return request;
	}

	private void restoreRequestAttributes(HttpServletRequest request,
			Map<String, Object> attributesSnapshot) {
		
		logger.debug("Restoring snapshot of request attributes after include . ");
		HashSet hashSet = new HashSet();
		Enumeration attrNames = request.getAttributeNames();
		while(attrNames.hasMoreElements()){
			String attrName = (String)attrNames.nextElement();
			if(cleanupAfterIncludeFlag || attrName.startsWith("org.springframework.web.servlet")){
				hashSet.add(attrName);
			}
		}
		
		for(Iterator iterator = hashSet.iterator() ; iterator.hasNext() ; ){
			String attrName = (String) iterator.next();
			Object obj = attributesSnapshot.get(attrName);
			if(obj == null){
				if(logger.isDebugEnabled()){
					logger.debug("Removing attribute [" + attrName + "] after Include . ");
				}
				request.removeAttribute(attrName);
			}
			else if(obj != request.getAttribute(attrName)){
				if(logger.isDebugEnabled()){
					logger.debug("Restore original value of attribute [" + attrName + "] after Include . ");
				}
				request.setAttribute(attrName, obj);
			}
		}
		
	}

	public void resetRequestAttributes(HttpServletRequest request) {
		
		request.setAttribute(MESSAGES_PROVIDER_ATTRIBUTE, messagesProvider);
		request.setAttribute(REFERER_RESOLVER_ATTRIBUTE, refererResolver); 
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, localeResolver); 
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, themeResolver); 
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 
		
	}

	private Object getThemeSource() {
		
		if(getWebApplicationContext() instanceof ThemeSource){
			return (ThemeSource)getWebApplicationContext();
		}
		
		return null;
	}
	
	
	protected long getLastModified(HttpServletRequest request) {
		Object obj ;
		if(this.logger.isDebugEnabled()){
			obj = Utils.getPathHelper().getRequestUri(request);
			this.logger.debug("MainServlet with name '" + getServletName() + "' determining Last-Modified value for [" + obj + "]");
		}
		try {
			obj = getHandler(request , true); //"obj"=HandlerExecutionChain
			if((obj==null) || ((HandlerExecutionChain)obj).getHandler()==null){
				this.logger.debug("No handler found in getLastModified");
				return -1L ;
			}
			
			HandlerAdapter handlerAdapter = getHandlerAdapter(((HandlerExecutionChain)obj).getHandler());
			long ll = handlerAdapter.getLastModified(request, ((HandlerExecutionChain)obj).getHandler()); //ll=-1
			if(this.logger.isDebugEnabled()){
				String str = Utils.getPathHelper().getRequestUri(request); //str="/weather/testRedirectEnd.do"
				this.logger.debug("Last-Modified value for [" + str + "] is: " + ll);
			}
			return ll ;
		} catch (Exception e) {
			this.logger.debug("Exception thrown in getLastModified" , e);
		}
		return -1L ;
	}
	
	
	protected LocaleContext buildLocaleContext(final HttpServletRequest request){
		return new LocaleContext(){
			public Locale getLocale(){
				return MainServlet.a(MainServlet.this).resolveLocale(request);
			}
			
			public String toString(){
				return getLocale().toString();
			}
		};
	}
	
	
	public String getServletContextAttributeName(){
		return MainServlet.class.getName() + ".CONTEXT." + getServletName();
	}
	
	public void init(ServletConfig servletConfig) throws ServletException {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		if(classLoader == null){
			classLoader = MainServlet.class.getClassLoader();
		}
		synchronized(System.class){
			System.out.println("old logger: " + getURL(LogFactory.getLog("").getClass()));
			LogFactory.release(classLoader);
			System.out.println("new logger: " + getURL(LogFactory.getLog("").getClass()));
		}
		
		super.init(servletConfig);
	}
	
	private URL getURL(Class clazz){
		String str = clazz.getSimpleName();
		return clazz.getResource(str+".class");
	}

	protected void onRefresh(ApplicationContext context){
		initStrategies(context);
		getServletContext().setAttribute(MainServlet.class.getName(), this);
		getServletContext().setAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);  // 解决自定义JS标签问题
	}
	
	/** 初始化相关默认类 */
	private void initStrategies(ApplicationContext context) {
		
		initMessageProvider(context);
		initMultipartResolver(context);
		initRefererResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMapping(context);
		initHandlerAdapter(context);
		initHandlerExceptionResolver(context);
		initViewResolver(context);
		
	}

	private void initViewResolver(ApplicationContext context) {
		
		viewResolvers = null ;
		Object obj ;
		if(!useMimeViewResolverFlag){
			/*obj = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, ViewResolver.class, true , false);
			if(!((Map)obj).isEmpty()){
				viewResolvers = new ArrayList<ViewResolver>(((Map)obj).values());
				OrderComparator.sort(viewResolvers);
			}
			if(viewResolvers == null){
				throw new IllegalArgumentException("No ViewResolvers found in servlet '" + getServletName() + "'");
			}*/
		}
		else {
			viewResolvers = new ArrayList<ViewResolver>();
			obj = new MimeTypesViewResolver();
			((MimeTypesViewResolver)obj).setApplicationContext(context);
			viewResolvers.add((ViewResolver) obj);
			if(logger.isDebugEnabled()){
				logger.debug("Use MimeViewResolver for servlet '" + getServletName() + "'");
			}
		}
		
	}

	private void initHandlerExceptionResolver(ApplicationContext context) {
		
		Map maps = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class , true , false);
		
		if(!maps.isEmpty()){
			handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(maps.values());
			OrderComparator.sort(handlerExceptionResolvers);
		}
		
		if(handlerExceptionResolvers == null){
			handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>();
			handlerExceptionResolvers.add(new DefaultHandlerExceptionResolver());
			if(logger.isDebugEnabled()){
				logger.debug("No handlerExceptionResolvers found in servlet '" + getServletName() + "',use default: " + handlerExceptionResolvers);
			}
		}
	}

	private void initHandlerAdapter(ApplicationContext context) {
		
		handlerAdapters = null;
		Map maps = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
		
		if(!maps.isEmpty()){
			handlerAdapters = new ArrayList<HandlerAdapter>(maps.values());
			OrderComparator.sort(handlerAdapters);
		}
		
		if(handlerAdapters == null){
			handlerAdapters = new ArrayList<HandlerAdapter>();
			handlerAdapters.add(new HttpRequestHandlerAdapter());
			handlerAdapters.add(new SimpleControllerHandlerAdapter());
			if(logger.isDebugEnabled()){
				logger.debug("No HandlerAdapters found in servlet '" + getServletName() +"',use default: " + handlerAdapters );
			}
		}
		
	}

	private void initHandlerMapping(ApplicationContext context) {
		
		handlerMappings = null;
		Map maps = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		
		if(!maps.isEmpty()){
			handlerMappings = new ArrayList<HandlerMapping>(maps.values());
			OrderComparator.sort(handlerMappings);
		}
		
		/*if(handlerMappings==null){
			throw new IllegalArgumentException("No HandlerMapping found in servlet '" + getServletName() + "'");
		}*/
	}

	private void initThemeResolver(ApplicationContext context) {
		
		Map maps = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ThemeResolver.class, true, false);
		
		if(!maps.isEmpty()){
			if(maps.size()>1){
				throw new IllegalArgumentException("more than one ThemeResolver found . ");
			}
			themeResolver = (ThemeResolver) maps.values().iterator().next();
		}
		else{
			themeResolver = new SmartThemeResolver();
		}
		if(logger.isDebugEnabled()){
			logger.debug("Using themeResolver [" + themeResolver + "]");
		}
	}

	private void initLocaleResolver(ApplicationContext context) {
		
		Map maps = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, LocaleResolver.class, true, false);
		
		if(!maps.isEmpty()){
			if(maps.size()>1){
				throw new IllegalArgumentException("more than one LocaleResolver found . ");
			}
			localeResolver = (LocaleResolver) maps.values().iterator().next();
		}
		else{
			localeResolver = new SmartLocaleResolver();
		}
		if(logger.isDebugEnabled()){
			logger.debug("Using LocaleResolver [" + localeResolver + "]");
		}
	}

	private void initRefererResolver(ApplicationContext context) {
		
		Map maps = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, RefererResolver.class, true, false);
		
		if(!maps.isEmpty()){
			if(maps.size()>1){
				throw new IllegalArgumentException("more than one RefererResolver found . ");
			}
			refererResolver = (RefererResolver) maps.values().iterator().next();
		}
		else{
			refererResolver = new DefaultRefererResolver();
		}
		if(logger.isDebugEnabled()){
			logger.debug("Using RefererResolver [" + refererResolver + "]");
		}
	}

	private void initMultipartResolver(ApplicationContext context) {
		
		Map maps = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, MultipartResolver.class, true, false);
		
		if(!maps.isEmpty()){
			if(maps.size()>1){
				throw new IllegalArgumentException("more than one MultipartResolver found . ");
			}
			multipartResolver = (MultipartResolver) maps.values().iterator().next();
		}
		else{
			CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
			commonsMultipartResolver.setMaxUploadSize(1024000L);
			multipartResolver = commonsMultipartResolver ;
		}
		if(logger.isDebugEnabled()){
			logger.debug("Using MultipartResolver [" + multipartResolver +"]");
		}
	}

	private void initMessageProvider(ApplicationContext context) {
		
		Map maps = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, MessagesProvider.class, true, false);
		
		if(!maps.isEmpty()){
			if(maps.size()>1){
				throw new IllegalArgumentException("more than one MessageProdiver found . ");
			}
			messagesProvider = (MessagesProvider)maps.values().iterator().next();
			if(logger.isDebugEnabled()){
				logger.debug("Using MessagesProvider [" + messagesProvider +"]");
			}
		}
	}
	
	
	protected WebApplicationContext createWebApplicationContext(ApplicationContext parent){
		
		Class clazz = getContextClass();
		if(logger.isDebugEnabled()){
			logger.debug("Servlet with name '" + getServletName() + 
			        "' will try to create custom WebApplicationContext context of class '" + 
			        clazz.getName() + "'" + ", using parent context [" + parent + "]");
		}
		if(!ConfigurableWebApplicationContext.class.isAssignableFrom(clazz)){
			throw new ApplicationContextException(
			        "Fatal initialization error in servlet with name '" + getServletName() + 
			        "': custom WebApplicationContext class [" + clazz.getName() + 
			        "] is not of type ConfigurableWebApplicationContext");
		}
		
		String[] arrayOfString = StringUtils.delimitedListToStringArray(getContextConfigLocation(), "+"); //[classpath:/config/MainTest.xml]
		ConfigurableWebApplicationContext configurableWebApplicationContext = null; 
		for(int i = 0 ; i < arrayOfString.length ; i++){
			logger.info("loading level " + i + ": " + arrayOfString[i]);
			configurableWebApplicationContext = (ConfigurableWebApplicationContext)BeanUtils.instantiate(clazz);
			ServletContext servletContext = getServletContext();
			if(servletContext.getMajorVersion()==2 && servletContext.getMinorVersion()<5){
				String str = servletContext.getServletContextName();
				if(str!=null){
					configurableWebApplicationContext.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX+str+"."+getServletName());
				}
				else{
					configurableWebApplicationContext.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX+getServletName());
				}
			}
			else{
				configurableWebApplicationContext.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX+servletContext.getContextPath()+"/"+getServletName());
			}
			
			configurableWebApplicationContext.setParent(parent);
			configurableWebApplicationContext.setServletContext(getServletContext());
			configurableWebApplicationContext.setServletConfig(getServletConfig());
			configurableWebApplicationContext.setNamespace(getNamespace());
			configurableWebApplicationContext.setConfigLocation(arrayOfString[i]);
			
			postProcessWebApplicationContext(configurableWebApplicationContext);
			configurableWebApplicationContext.refresh();
			//defaultInterceptors
			parent = configurableWebApplicationContext ;
			
		}
		
		
		configurableWebApplicationContext.addApplicationListener(new SourceFilteringListener(configurableWebApplicationContext, new SourceApplicationListener()));
		
		return configurableWebApplicationContext;
	}
	
	public void destroy(){
		
		getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
		logger.info("destroying MainServlet ... ");
		long time = System.currentTimeMillis();
		Object obj = getWebApplicationContext();
		
		while(obj != null){
			if(obj instanceof ConfigurableApplicationContext){
				logger.info("destroying app context : " + ((ApplicationContext)obj).getDisplayName());
				((ConfigurableApplicationContext)obj).close();
			}
			else{
				logger.warn("app context is not closable: " + ((ApplicationContext)obj).getDisplayName());
			}
			obj = ((ApplicationContext)obj).getParent();
		}
		
		logger.info("Destroyed Spring FrameworkServlet '" + getServletName() + "', spent: " + (System.currentTimeMillis()-time)+" ms");
	}
	
	 private class SourceApplicationListener  implements ApplicationListener
	 {
	
	     public void SourceApplicationListener(ContextRefreshedEvent contextrefreshedevent)
	     {
	    	 mainServlet.onApplicationEvent(contextrefreshedevent);
	     }
	
	     public  void onApplicationEvent(ApplicationEvent applicationevent)
	     {
	    	 SourceApplicationListener((ContextRefreshedEvent)applicationevent);
	     }
	
	     final MainServlet mainServlet;
	
	     private SourceApplicationListener()
	     {
	    	 super();
	    	 mainServlet = MainServlet.this;
	     }
	
	     SourceApplicationListener(SourceApplicationListener a1)
	     {
	         this();
	     }
		
	 }
 
	 static LocaleResolver a(MainServlet mainservlet)
	 {
	     return mainservlet.localeResolver;
	 }
	
}
